#include "mcl/writer.h"
#include "mcl/da.h"

#include <stdio.h>

struct writer new_writer_from_file(FILE *fp)
{
	return (struct writer){.type = WRITER_FILE, .via.fp = fp};
}

struct writer new_writer_from_buffer(void)
{
	return (struct writer){.type = WRITER_BUFFER, .via.buf = DA_INIT};
}

void writer_deinit(struct writer *w)
{
	switch (w->type) {
	case WRITER_FILE:
		fclose(w->via.fp);
		break;
	case WRITER_BUFFER:
		DA_DELETE(w->via.buf);
		break;
	}

	w->type = WRITER_BUFFER;
	w->via.buf = (struct dstr)DA_INIT;
}

void write_bytes(struct writer *w, char const *start, usize len)
{
	switch (w->type) {
	case WRITER_FILE:
		fwrite(start, len, 1, w->via.fp);
		break;
	case WRITER_BUFFER:
		DA_RESERVE(w->via.buf, w->via.buf.len + len);
		memmove(w->via.buf.data + w->via.buf.len, start, len);
		w->via.buf.len += len;
		break;
	}
}

void write_cstr(struct writer *w, char const *s)
{
	write_bytes(w, s, strlen(s));
}

void write_dstr(struct writer *w, struct dstr const *dstr)
{
	write_bytes(w, dstr->data, dstr->len);
}

void write_dstr_view(struct writer *w, struct dstr_view const *dstr)
{
	write_bytes(w, dstr->data, dstr->len);
}

void write_bool(struct writer *w, bool b)
{
	if (b) {
		write_cstr(w, "true");
	} else {
		write_cstr(w, "false");
	}
}

void write_pointer(struct writer *w, void *p)
{
	const usize maxlen = 30;

	switch (w->type) {
	case WRITER_FILE:
		fprintf(w->via.fp, "%p", p);
		break;
	case WRITER_BUFFER:
		DA_RESERVE(w->via.buf, w->via.buf.len + maxlen);
		w->via.buf.len += snprintf(
			w->via.buf.data + w->via.buf.len,
			maxlen,
			"%p",
			p);
		break;
	}
}

void write_i64(struct writer *w, int64_t x)
{
	const usize maxlen = 30;

	switch (w->type) {
	case WRITER_FILE:
		fprintf(w->via.fp, "%" PRIi64, x);
		break;
	case WRITER_BUFFER:
		DA_RESERVE(w->via.buf, w->via.buf.len + maxlen);
		w->via.buf.len += snprintf(
			w->via.buf.data + w->via.buf.len,
			maxlen,
			"%" PRIi64,
			x);
		break;
	}
}
